home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 001-025 / disk_022 / lemacs / line.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  19KB  |  517 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include        <stdio.h>
  15. #include    "estruct.h"
  16. #include        "edef.h"
  17.  
  18. #define NBLOCK  16                      /* Line block chunk size        */
  19. #define KBLOCK  1024                    /* Kill buffer block size       */
  20.  
  21. char    *kbufp  = NULL;                 /* Kill buffer data             */
  22. unsigned kused   = 0;                   /* # of bytes used in KB        */
  23. unsigned ksize   = 0;                   /* # of bytes allocated in KB   */
  24.  
  25. /*
  26.  * This routine allocates a block of memory large enough to hold a LINE
  27.  * containing "used" characters. The block is always rounded up a bit. Return
  28.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  29.  * message in the message line if no space.
  30.  */
  31. LINE    *
  32. lalloc(used)
  33. register int    used;
  34. {
  35.         register LINE   *lp;
  36.         register int    size;
  37.     char *malloc();
  38.  
  39.         size = (used+NBLOCK-1) & ~(NBLOCK-1);
  40.         if (size == 0)                          /* Assume that an empty */
  41.                 size = NBLOCK;                  /* line is for type-in. */
  42.         if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  43.                 mlwrite("Cannot allocate %d bytes", size);
  44.                 return (NULL);
  45.         }
  46.         lp->l_size = size;
  47.         lp->l_used = used;
  48.         return (lp);
  49. }
  50.  
  51. /*
  52.  * Delete line "lp". Fix all of the links that might point at it (they are
  53.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  54.  * might be in. Release the memory. The buffers are updated too; the magic
  55.  * conditions described in the above comments don't hold here.
  56.  */
  57. lfree(lp)
  58. register LINE   *lp;
  59. {
  60.         register BUFFER *bp;
  61.         register WINDOW *wp;
  62.  
  63.         wp = wheadp;
  64.         while (wp != NULL) {
  65.                 if (wp->w_linep == lp)
  66.                         wp->w_linep = lp->l_fp;
  67.                 if (wp->w_dotp  == lp) {
  68.                         wp->w_dotp  = lp->l_fp;
  69.                         wp->w_doto  = 0;
  70.                 }
  71.                 if (wp->w_markp == lp) {
  72.                         wp->w_markp = lp->l_fp;
  73.                         wp->w_marko = 0;
  74.                 }
  75.                 wp = wp->w_wndp;
  76.         }
  77.         bp = bheadp;
  78.         while (bp != NULL) {
  79.                 if (bp->b_nwnd == 0) {
  80.                         if (bp->b_dotp  == lp) {
  81.                                 bp->b_dotp = lp->l_fp;
  82.                                 bp->b_doto = 0;
  83.                         }
  84.                         if (bp->b_markp == lp) {
  85.                                 bp->b_markp = lp->l_fp;
  86.                                 bp->b_marko = 0;
  87.                         }
  88.                 }
  89.                 bp = bp->b_bufp;
  90.         }
  91.         lp->l_bp->l_fp = lp->l_fp;
  92.         lp->l_fp->l_bp = lp->l_bp;
  93.         free((char *) lp);
  94. }
  95.  
  96. /*
  97.  * This routine gets called when a character is changed in place in the current
  98.  * buffer. It updates all of the required flags in the buffer and window
  99.  * system. The flag used is passed as an argument; if the buffer is being
  100.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  101.  * mode line needs to be updated (the "*" has to be set).
  102.  */
  103. lchange(flag)
  104. register int    flag;
  105. {
  106.         register WINDOW *wp;
  107.  
  108.         if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
  109.                 flag = WFHARD;
  110.         if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
  111.                 flag |= WFMODE;                 /* update mode lines.   */
  112.                 curbp->b_flag |= BFCHG;
  113.         }
  114.         wp = wheadp;
  115.         while (wp != NULL) {
  116.                 if (wp->w_bufp == curbp)
  117.                         wp->w_flag |= flag;
  118.                 wp = wp->w_wndp;
  119.         }
  120. }
  121.  
  122. insspace(f, n)    /* insert spaces forward into text */
  123.  
  124. int f, n;    /* default flag and numeric argument */
  125.  
  126. {
  127.     linsert(n, ' ');
  128.     backchar(f, n);
  129. }
  130.  
  131. /*
  132.  * Insert "n" copies of the character "c" at the current location of dot. In
  133.  * the easy case all that happens is the text is stored in the line. In the
  134.  * hard case, the line has to be reallocated. When the window list is updated,
  135.  * take special care; I screwed it up once. You always update dot in the
  136.  * current window. You update mark, and a dot in another window, if it is
  137.  * greater than the place where you did the insert. Return TRUE if all is
  138.  * well, and FALSE on errors.
  139.  */
  140. linsert(n, c)
  141. {
  142.         register char   *cp1;
  143.         register char   *cp2;
  144.         register LINE   *lp1;
  145.         register LINE   *lp2;
  146.         register LINE   *lp3;
  147.         register int    doto;
  148.         register int    i;
  149.         register WINDOW *wp;
  150.  
  151.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  152.         return(rdonly());    /* we are in read only mode    */
  153.         lchange(WFEDIT);
  154.         lp1 = curwp->w_dotp;                    /* Current line         */
  155.         if (lp1 == curbp->b_linep) {            /* At the end: special  */
  156.                 if (curwp->w_doto != 0) {
  157.                         mlwrite("bug: linsert");
  158.                         return (FALSE);
  159.                 }
  160.                 if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  161.                         return (FALSE);
  162.                 lp3 = lp1->l_bp;                /* Previous line        */
  163.                 lp3->l_fp = lp2;                /* Link in              */
  164.                 lp2->l_fp = lp1;
  165.                 lp1->l_bp = lp2;
  166.                 lp2->l_bp = lp3;
  167.                 for (i=0; i<n; ++i)
  168.                         lp2->l_text[i] = c;
  169.                 curwp->w_dotp = lp2;
  170.                 curwp->w_doto = n;
  171.                 return (TRUE);
  172.         }
  173.         doto = curwp->w_doto;                   /* Save for later.      */
  174.         if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
  175.                 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  176.                         return (FALSE);
  177.                 cp1 = &lp1->l_text[0];
  178.                 cp2 = &lp2->l_text[0];
  179.                 while (cp1 != &lp1->l_text[doto])
  180.                         *cp2++ = *cp1++;
  181.                 cp2 += n;
  182.                 while (cp1 != &lp1->l_text[lp1->l_used])
  183.                         *cp2++ = *cp1++;
  184.                 lp1->l_bp->l_fp = lp2;
  185.                 lp2->l_fp = lp1->l_fp;
  186.                 lp1->l_fp->l_bp = lp2;
  187.                 lp2->l_bp = lp1->l_bp;
  188.                 free((char *) lp1);
  189.         } else {                                /* Easy: in place       */
  190.                 lp2 = lp1;                      /* Pretend new line     */
  191.                 lp2->l_used += n;
  192.                 cp2 = &lp1->l_text[lp1->l_used];
  193.                 cp1 = cp2-n;
  194.                 while (cp1 != &lp1->l_text[doto])
  195.                         *--cp2 = *--cp1;
  196.         }
  197.         for (i=0; i<n; ++i)                     /* Add the characters   */
  198.                 lp2->l_text[doto+i] = c;
  199.         wp = wheadp;                            /* Update windows       */
  200.         while (wp != NULL) {
  201.                 if (wp->w_linep == lp1)
  202.                         wp->w_linep = lp2;
  203.                 if (wp->w_dotp == lp1) {
  204.                         wp->w_dotp = lp2;
  205.                         if (wp==curwp || wp->w_doto>doto)
  206.                                 wp->w_doto += n;
  207.                 }
  208.                 if (wp->w_markp == lp1) {
  209.                         wp->w_markp = lp2;
  210.                         if (wp->w_marko > doto)
  211.                                 wp->w_marko += n;
  212.                 }
  213.                 wp = wp->w_wndp;
  214.         }
  215.         return (TRUE);
  216. }
  217.  
  218. /*
  219.  * Insert a newline into the buffer at the current location of dot in the
  220.  * current window. The funny ass-backwards way it does things is not a botch;
  221.  * it just makes the last line in the file not a special case. Return TRUE if
  222.  * everything works out and FALSE on error (memory allocation failure). The
  223.  * update of dot and mark is a bit easier then in the above case, because the
  224.  * split forces more updating.
  225.  */
  226. lnewline()
  227. {
  228.         register char   *cp1;
  229.         register char   *cp2;
  230.         register LINE   *lp1;
  231.         register LINE   *lp2;
  232.         register int    doto;
  233.         register WINDOW *wp;
  234.  
  235.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  236.         return(rdonly());    /* we are in read only mode    */
  237.         lchange(WFHARD);
  238.         lp1  = curwp->w_dotp;                   /* Get the address and  */
  239.         doto = curwp->w_doto;                   /* offset of "."        */
  240.         if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
  241.                 return (FALSE);
  242.         cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
  243.         cp2 = &lp2->l_text[0];
  244.         while (cp1 != &lp1->l_text[doto])
  245.                 *cp2++ = *cp1++;
  246.         cp2 = &lp1->l_text[0];
  247.         while (cp1 != &lp1->l_text[lp1->l_used])
  248.                 *cp2++ = *cp1++;
  249.         lp1->l_used -= doto;
  250.         lp2->l_bp = lp1->l_bp;
  251.         lp1->l_bp = lp2;
  252.         lp2->l_bp->l_fp = lp2;
  253.         lp2->l_fp = lp1;
  254.         wp = wheadp;                            /* Windows              */
  255.         while (wp != NULL) {
  256.                 if (wp->w_linep == lp1)
  257.                         wp->w_linep = lp2;
  258.                 if (wp->w_dotp == lp1) {
  259.                         if (wp->w_doto < doto)
  260.                                 wp->w_dotp = lp2;
  261.                         else
  262.                                 wp->w_doto -= doto;
  263.                 }
  264.                 if (wp->w_markp == lp1) {
  265.                         if (wp->w_marko < doto)
  266.                                 wp->w_markp = lp2;
  267.                         else
  268.                                 wp->w_marko -= doto;
  269.                 }
  270.                 wp = wp->w_wndp;
  271.         }
  272.         return (TRUE);
  273. }
  274.  
  275. /*
  276.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  277.  * with end of lines, etc. It returns TRUE if all of the characters were
  278.  * deleted, and FALSE if they were not (because dot ran into the end of the
  279.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  280.  */
  281. ldelete(n, kflag)
  282. {
  283.         register char   *cp1;
  284.         register char   *cp2;
  285.         register LINE   *dotp;
  286.         register int    doto;
  287.         register int    chunk;
  288.         register WINDOW *wp;
  289.  
  290.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  291.         return(rdonly());    /* we are in read only mode    */
  292.         while (n != 0) {
  293.                 dotp = curwp->w_dotp;
  294.                 doto = curwp->w_doto;
  295.                 if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
  296.                         return (FALSE);
  297.                 chunk = dotp->l_used-doto;      /* Size of chunk.       */
  298.                 if (chunk > n)
  299.                         chunk = n;
  300.                 if (chunk == 0) {               /* End of line, merge.  */
  301.                         lchange(WFHARD);
  302.                         if (ldelnewline() == FALSE
  303.                         || (kflag!=FALSE && kinsert('\n')==FALSE))
  304.                                 return (FALSE);
  305.                         --n;
  306.                         continue;
  307.                 }
  308.                 lchange(WFEDIT);
  309.                 cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
  310.                 cp2 = cp1 + chunk;
  311.                 if (kflag != FALSE) {           /* Kill?                */
  312.                         while (cp1 != cp2) {
  313.                                 if (kinsert(*cp1) == FALSE)
  314.                                         return (FALSE);
  315.                                 ++cp1;
  316.                         }
  317.                         cp1 = &dotp->l_text[doto];
  318.                 }
  319.                 while (cp2 != &dotp->l_text[dotp->l_used])
  320.                         *cp1++ = *cp2++;
  321.                 dotp->l_used -= chunk;
  322.                 wp = wheadp;                    /* Fix windows          */
  323.                 while (wp != NULL) {
  324.                         if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  325.                                 wp->w_doto -= chunk;
  326.                                 if (wp->w_doto < doto)
  327.                                         wp->w_doto = doto;
  328.                         }
  329.                         if (wp->w_markp==dotp && wp->w_marko>=doto) {
  330.                                 wp->w_marko -= chunk;
  331.                                 if (wp->w_marko < doto)
  332.                                         wp->w_marko = doto;
  333.                         }
  334.                         wp = wp->w_wndp;
  335.                 }
  336.                 n -= chunk;
  337.         }
  338.         return (TRUE);
  339. }
  340.  
  341. /*
  342.  * Delete a newline. Join the current line with the next line. If the next line
  343.  * is the magic header line always return TRUE; merging the last line with the
  344.  * header line can be thought of as always being a successful operation, even
  345.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  346.  * can be done by shuffling data around. Hard cases require that lines be moved
  347.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  348.  * "ldelete" only.
  349.  */
  350. ldelnewline()
  351. {
  352.         register char   *cp1;
  353.         register char   *cp2;
  354.         register LINE   *lp1;
  355.         register LINE   *lp2;
  356.         register LINE   *lp3;
  357.         register WINDOW *wp;
  358.  
  359.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  360.         return(rdonly());    /* we are in read only mode    */
  361.         lp1 = curwp->w_dotp;
  362.         lp2 = lp1->l_fp;
  363.         if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
  364.                 if (lp1->l_used == 0)           /* Blank line.          */
  365.                         lfree(lp1);
  366.                 return (TRUE);
  367.         }
  368.         if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  369.                 cp1 = &lp1->l_text[lp1->l_used];
  370.                 cp2 = &lp2->l_text[0];
  371.                 while (cp2 != &lp2->l_text[lp2->l_used])
  372.                         *cp1++ = *cp2++;
  373.                 wp = wheadp;
  374.                 while (wp != NULL) {
  375.                         if (wp->w_linep == lp2)
  376.                                 wp->w_linep = lp1;
  377.                         if (wp->w_dotp == lp2) {
  378.                                 wp->w_dotp  = lp1;
  379.                                 wp->w_doto += lp1->l_used;
  380.                         }
  381.                         if (wp->w_markp == lp2) {
  382.                                 wp->w_markp  = lp1;
  383.                                 wp->w_marko += lp1->l_used;
  384.                         }
  385.                         wp = wp->w_wndp;
  386.                 }
  387.                 lp1->l_used += lp2->l_used;
  388.                 lp1->l_fp = lp2->l_fp;
  389.                 lp2->l_fp->l_bp = lp1;
  390.                 free((char *) lp2);
  391.                 return (TRUE);
  392.         }
  393.         if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  394.                 return (FALSE);
  395.         cp1 = &lp1->l_text[0];
  396.         cp2 = &lp3->l_text[0];
  397.         while (cp1 != &lp1->l_text[lp1->l_used])
  398.                 *cp2++ = *cp1++;
  399.         cp1 = &lp2->l_text[0];
  400.         while (cp1 != &lp2->l_text[lp2->l_used])
  401.                 *cp2++ = *cp1++;
  402.         lp1->l_bp->l_fp = lp3;
  403.         lp3->l_fp = lp2->l_fp;
  404.         lp2->l_fp->l_bp = lp3;
  405.         lp3->l_bp = lp1->l_bp;
  406.         wp = wheadp;
  407.         while (wp != NULL) {
  408.                 if (wp->w_linep==lp1 || wp->w_linep==lp2)
  409.                         wp->w_linep = lp3;
  410.                 if (wp->w_dotp == lp1)
  411.                         wp->w_dotp  = lp3;
  412.                 else if (wp->w_dotp == lp2) {
  413.                         wp->w_dotp  = lp3;
  414.                         wp->w_doto += lp1->l_used;
  415.                 }
  416.                 if (wp->w_markp == lp1)
  417.                         wp->w_markp  = lp3;
  418.                 else if (wp->w_markp == lp2) {
  419.                         wp->w_markp  = lp3;
  420.                         wp->w_marko += lp1->l_used;
  421.                 }
  422.                 wp = wp->w_wndp;
  423.         }
  424.         free((char *) lp1);
  425.         free((char *) lp2);
  426.         return (TRUE);
  427. }
  428.  
  429. /*
  430.  * Delete all of the text saved in the kill buffer. Called by commands when a
  431.  * new kill context is being created. The kill buffer array is released, just
  432.  * in case the buffer has grown to immense size. No errors.
  433.  */
  434. kdelete()
  435. {
  436.         if (kbufp != NULL) {
  437.                 free((char *) kbufp);
  438.                 kbufp = NULL;
  439.                 kused = 0;
  440.                 ksize = 0;
  441.         }
  442. }
  443.  
  444. /*
  445.  * Insert a character to the kill buffer, enlarging the buffer if there isn't
  446.  * any room. Always grow the buffer in chunks, on the assumption that if you
  447.  * put something in the kill buffer you are going to put more stuff there too
  448.  * later. Return TRUE if all is well, and FALSE on errors.
  449.  */
  450.  
  451. kinsert(c)
  452. {
  453.         register char   *nbufp;
  454.     char *realloc();
  455.     char *malloc();
  456.  
  457.         if (kused == ksize) {
  458.         if (ksize == 0)    /* first time through? */
  459.             nbufp = malloc(KBLOCK);    /* alloc the first block */
  460.         else    /* or re allocate a bigger block */
  461.             nbufp = realloc(kbufp, ksize+KBLOCK);
  462.         if (nbufp == NULL)        /* abort if it fails */
  463.             return(FALSE);
  464.                 kbufp  = nbufp;        /* point our global at it */
  465.                 ksize += KBLOCK;    /* and adjust the size */
  466.         }
  467.         kbufp[kused++] = c;
  468.         return (TRUE);
  469. }
  470.  
  471. /*
  472.  * This function gets characters from the kill buffer. If the character index
  473.  * "n" is off the end, it returns "-1". This lets the caller just scan along
  474.  * until it gets a "-1" back.
  475.  */
  476. kremove(n)
  477. {
  478.         if (n >= kused)
  479.                 return (-1);
  480.         else
  481.                 return (kbufp[n] & 0xFF);
  482. }
  483.  
  484. #if    LATTICE || MANX
  485. /*    we need to have the following functions to manage memory that
  486.     don't exist under Lattice                    */
  487.  
  488.  
  489. char *realloc(ptr, size)    /* re-allocate a memory chunk to a
  490.                    different size, copying what can
  491.                    by copied                */
  492.  
  493. char *ptr;    /* pointer to the original block */
  494. unsigned size;    /* # of bytes needed in new block */
  495.  
  496. {
  497.     char *newptr;    /* pointer to new block */
  498.     unsigned csize;    /* size of area to copy from old buffer to new */
  499.     char *malloc();
  500.  
  501.     newptr = malloc(size);        /* get the new block */
  502.     if (newptr == NULL)        /* if malloc fails....*/
  503.         return(NULL);
  504.  
  505.     csize = ksize;    /******THIS IS A CHEAT SINCE WE CAN NOT GET
  506.                    AT THE SIZE OF THE MALLOCED BLOCK!!
  507.                    DO NOT USE THIS FUNCTION GENERICALLY!!!*/
  508.     if (csize > size)    /* we need to copy some stuff from */
  509.         csize = size;    /* the old buffer to the new    */
  510.  
  511.     movmem(ptr, newptr, csize);    /* copy the availible bytes */
  512.     free(ptr);            /* dump the old buffer */
  513.     return(newptr);            /* and return the new */
  514. }
  515. #endif
  516.  
  517.